feat(browseros): --gn-arg overrides for configure#1516
Conversation
Repeatable 'browseros build --gn-arg key=value' passthrough, accepted in preset/profile, modules-profile, and direct (--modules/phase-flag) modes. ConfigureModule appends the values last in args.gn under a '# --gn-arg overrides' comment, so GN last-write-wins makes them authoritative over the committed flags files — no more editing config/gn/*.gn to flip symbol_level or dcheck_always_on while iterating on chromium_patches. Values are validated (key=value shape) but pass through verbatim; they are per-invocation by design (no profile key). Overrides surface in --show-plan headers, the run banner, and a configure-time log line so a build log always reveals a non-standard configuration.
|
Greptile (@greptileai) review |
|
Claude (@claude) review |
|
Claude finished Nikhil (@shadowfax92)'s task in 2m 43s —— View job Code Review — feat(browseros): --gn-arg overrides for configure
OverallWell-designed feature. All three build modes (preset, modules-profile, direct) thread
|
Greptile SummaryThis PR adds a repeatable
Confidence Score: 4/5Safe to merge; the feature is well-scoped, thoroughly tested, and the core append-last write to args.gn is straightforward and correct. The implementation is clean and test coverage is thorough across all three build modes. The no-effect warning (configure absent from plan) fires only at execution time and is silent during --show-plan, so a user previewing a sliced plan that skips configure will see the GN overrides in the header without any hint they will be ignored. The regex also allows whitespace-only values through validation, which GN would reject with a generic error. Neither is a blocking defect, but both are worth fixing for a better developer experience. cli/build.py — the no-effect warning placement and the regex value constraint. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CLI as CLI (build.py)
participant Parser as _parse_gn_args
participant Resolver as resolve_config / _resolve_preset
participant Ctx as Context
participant Configure as ConfigureModule.execute
participant ArgsGn as args.gn
CLI->>Parser: gn_arg list from --gn-arg flags
Parser-->>CLI: validated tuple[str,...]
alt preset / modules-profile mode
CLI->>Resolver: "_resolve_preset(extra_gn_args=...)"
Resolver->>Ctx: "Context(extra_gn_args=extra_gn_args)"
else direct mode
CLI->>Resolver: "resolve_config({extra_gn_args: ...})"
Resolver->>Ctx: "Context(extra_gn_args=extra_gn_args)"
end
Ctx-->>CLI: runs [(ctx, steps)]
CLI->>Configure: execute(ctx)
Configure->>ArgsGn: write flags_file + target_cpu + product_args
Configure->>ArgsGn: "append # --gn-arg overrides + extra_gn_args"
Configure->>Configure: gn gen out/dir
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CLI as CLI (build.py)
participant Parser as _parse_gn_args
participant Resolver as resolve_config / _resolve_preset
participant Ctx as Context
participant Configure as ConfigureModule.execute
participant ArgsGn as args.gn
CLI->>Parser: gn_arg list from --gn-arg flags
Parser-->>CLI: validated tuple[str,...]
alt preset / modules-profile mode
CLI->>Resolver: "_resolve_preset(extra_gn_args=...)"
Resolver->>Ctx: "Context(extra_gn_args=extra_gn_args)"
else direct mode
CLI->>Resolver: "resolve_config({extra_gn_args: ...})"
Resolver->>Ctx: "Context(extra_gn_args=extra_gn_args)"
end
Ctx-->>CLI: runs [(ctx, steps)]
CLI->>Configure: execute(ctx)
Configure->>ArgsGn: write flags_file + target_cpu + product_args
Configure->>ArgsGn: "append # --gn-arg overrides + extra_gn_args"
Configure->>Configure: gn gen out/dir
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/browseros/bos_build/cli/build.py:377-383
**No-effect warning silent during `--show-plan`**
The `--gn-arg has no effect` warning only fires during actual execution (after `build_runs()` is called on line 328), but `--show-plan` returns on line 277 before ever reaching the banner block. A user running `--preset release --from compile --gn-arg symbol_level=2 --show-plan` will see `GN arg overrides: symbol_level=2` in the plan header, with no indication that configure is absent from the sliced plan and the override is a no-op. The header lists the GN args truthfully, but a co-located note in `--show-plan` output would prevent confusion.
### Issue 2 of 2
packages/browseros/bos_build/cli/build.py:704
**Regex permits whitespace-only values**
`_GN_ARG_RE` uses `.+` for the value, which allows `key= ` (whitespace-only string) to pass validation. GN would reject or silently misbehave on such args depending on the expected type (e.g. boolean or integer args don't accept whitespace). Changing `.+` to `\S.*` (value must start with a non-whitespace character) would reject these before they cause a confusing `gn gen` error downstream.
```suggestion
_GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=\S.*\Z")
```
Reviews (1): Last reviewed commit: "feat(browseros): --gn-arg overrides for ..." | Re-trigger Greptile |
| if summary_ctx.extra_gn_args: | ||
| log_info(f"📍 GN arg overrides: {', '.join(summary_ctx.extra_gn_args)}") | ||
| if not any("configure" in run_steps for _, run_steps in runs): | ||
| log_warning( | ||
| "⚠️ --gn-arg has no effect: no run in this plan includes the " | ||
| "configure step, so args.gn is reused as-is" | ||
| ) |
There was a problem hiding this comment.
No-effect warning silent during
--show-plan
The --gn-arg has no effect warning only fires during actual execution (after build_runs() is called on line 328), but --show-plan returns on line 277 before ever reaching the banner block. A user running --preset release --from compile --gn-arg symbol_level=2 --show-plan will see GN arg overrides: symbol_level=2 in the plan header, with no indication that configure is absent from the sliced plan and the override is a no-op. The header lists the GN args truthfully, but a co-located note in --show-plan output would prevent confusion.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros/bos_build/cli/build.py
Line: 377-383
Comment:
**No-effect warning silent during `--show-plan`**
The `--gn-arg has no effect` warning only fires during actual execution (after `build_runs()` is called on line 328), but `--show-plan` returns on line 277 before ever reaching the banner block. A user running `--preset release --from compile --gn-arg symbol_level=2 --show-plan` will see `GN arg overrides: symbol_level=2` in the plan header, with no indication that configure is absent from the sliced plan and the override is a no-op. The header lists the GN args truthfully, but a co-located note in `--show-plan` output would prevent confusion.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| return tuple(s.strip() for s in value.split(",") if s.strip()) | ||
|
|
||
|
|
||
| _GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=.+\Z") |
There was a problem hiding this comment.
Regex permits whitespace-only values
_GN_ARG_RE uses .+ for the value, which allows key= (whitespace-only string) to pass validation. GN would reject or silently misbehave on such args depending on the expected type (e.g. boolean or integer args don't accept whitespace). Changing .+ to \S.* (value must start with a non-whitespace character) would reject these before they cause a confusing gn gen error downstream.
| _GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=.+\Z") | |
| _GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=\S.*\Z") |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros/bos_build/cli/build.py
Line: 704
Comment:
**Regex permits whitespace-only values**
`_GN_ARG_RE` uses `.+` for the value, which allows `key= ` (whitespace-only string) to pass validation. GN would reject or silently misbehave on such args depending on the expected type (e.g. boolean or integer args don't accept whitespace). Changing `.+` to `\S.*` (value must start with a non-whitespace character) would reject these before they cause a confusing `gn gen` error downstream.
```suggestion
_GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=\S.*\Z")
```
How can I resolve this? If you propose a fix, please make it concise.
Greptile SummaryThis PR adds a repeatable
Confidence Score: 4/5The change is well-scoped to the CLI and configure step; existing pipeline behaviour is untouched and the feature is per-invocation only. All three build modes correctly thread extra_gn_args to Context and on to args.gn; validation, show-plan headers, the runtime warning, and test coverage are thorough. Two minor code-quality observations exist — direct attribute mutation in the test helper rather than the constructor, and the regex admitting a bare carriage return in values — but neither affects correctness in practice. configure_test.py warrants a quick look at the make_context helper to confirm it supports extra_gn_args as a constructor parameter; everything else is straightforward. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI["browseros build --gn-arg key=value"]
PARSE["_parse_gn_args()\nRegex: ^[a-zA-Z_][a-zA-Z0-9_]*=.+\\Z"]
INVALID["log_error + Exit(1)"]
MODE{Build mode?}
PRESET["_resolve_preset()\nextra_gn_args threaded\nto build_runs()"]
MODULES["_resolve_modules_profile()\nextra_gn_args → resolve_config()"]
DIRECT["Direct mode\nresolve_config(cli_args)"]
CTX["Context.extra_gn_args\n= tuple[str, ...]"]
WARN{configure\nin run_steps?}
WARNING["log_warning: --gn-arg\nhas no effect"]
CONFIGURE["ConfigureModule.execute()"]
ARGSGN["args.gn written:\n1. flags file\n2. target_cpu\n3. product args\n4. # --gn-arg overrides\n5. extra_gn_args (last-write-wins)"]
GN["gn gen out_dir"]
CLI --> PARSE
PARSE -->|invalid| INVALID
PARSE -->|valid tuple| MODE
MODE -->|preset/profile| PRESET
MODE -->|modules profile| MODULES
MODE -->|modules/phase flags| DIRECT
PRESET --> CTX
MODULES --> CTX
DIRECT --> CTX
CTX --> WARN
WARN -->|No| WARNING
WARN -->|Yes| CONFIGURE
CONFIGURE --> ARGSGN
ARGSGN --> GN
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
CLI["browseros build --gn-arg key=value"]
PARSE["_parse_gn_args()\nRegex: ^[a-zA-Z_][a-zA-Z0-9_]*=.+\\Z"]
INVALID["log_error + Exit(1)"]
MODE{Build mode?}
PRESET["_resolve_preset()\nextra_gn_args threaded\nto build_runs()"]
MODULES["_resolve_modules_profile()\nextra_gn_args → resolve_config()"]
DIRECT["Direct mode\nresolve_config(cli_args)"]
CTX["Context.extra_gn_args\n= tuple[str, ...]"]
WARN{configure\nin run_steps?}
WARNING["log_warning: --gn-arg\nhas no effect"]
CONFIGURE["ConfigureModule.execute()"]
ARGSGN["args.gn written:\n1. flags file\n2. target_cpu\n3. product args\n4. # --gn-arg overrides\n5. extra_gn_args (last-write-wins)"]
GN["gn gen out_dir"]
CLI --> PARSE
PARSE -->|invalid| INVALID
PARSE -->|valid tuple| MODE
MODE -->|preset/profile| PRESET
MODE -->|modules profile| MODULES
MODE -->|modules/phase flags| DIRECT
PRESET --> CTX
MODULES --> CTX
DIRECT --> CTX
CTX --> WARN
WARN -->|No| WARNING
WARN -->|Yes| CONFIGURE
CONFIGURE --> ARGSGN
ARGSGN --> GN
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/browseros/bos_build/steps/setup/configure_test.py:71-74
The test sets `ctx.extra_gn_args` via direct attribute mutation after construction rather than passing the field through the constructor. Since `Context` is a non-frozen dataclass, this works, but `extra_gn_args` is a regular init parameter with a default, so it can be passed directly — keeping the context construction consistent with how all other fields are handled and making the intent clearer.
```suggestion
ctx = make_context(
chromium, root, architecture=architecture, build_type=build_type,
extra_gn_args=extra_gn_args,
)
```
### Issue 2 of 2
packages/browseros/bos_build/cli/build.py:704
**Regex accepts `\r` in values**
The pattern `.+` (without `re.DOTALL`) correctly rejects embedded newlines `\n`, but it does match carriage return `\r`. A value like `symbol_level=2\r` passes validation and is written verbatim into `args.gn`, which could cause a GN parse error on Unix. Replacing `.+` with `[^\r\n]+` closes this gap with no change to normal usage.
Reviews (2): Last reviewed commit: "feat(browseros): --gn-arg overrides for ..." | Re-trigger Greptile |
| return tuple(s.strip() for s in value.split(",") if s.strip()) | ||
|
|
||
|
|
||
| _GN_ARG_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*=.+\Z") |
There was a problem hiding this comment.
The pattern .+ (without re.DOTALL) correctly rejects embedded newlines \n, but it does match carriage return \r. A value like symbol_level=2\r passes validation and is written verbatim into args.gn, which could cause a GN parse error on Unix. Replacing .+ with [^\r\n]+ closes this gap with no change to normal usage.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros/bos_build/cli/build.py
Line: 704
Comment:
**Regex accepts `\r` in values**
The pattern `.+` (without `re.DOTALL`) correctly rejects embedded newlines `\n`, but it does match carriage return `\r`. A value like `symbol_level=2\r` passes validation and is written verbatim into `args.gn`, which could cause a GN parse error on Unix. Replacing `.+` with `[^\r\n]+` closes this gap with no change to normal usage.
How can I resolve this? If you propose a fix, please make it concise.
Summary
browseros build --gn-arg key=valuepassthrough, accepted in all three build modes (preset/profile, modules-profiles, and direct--modules/phase flags).ConfigureModuleappends the values last inargs.gnunder a# --gn-arg overridescomment — GN last-write-wins makes them authoritative over the committed flags files, so iterating onchromium_patchesno longer means editingconfig/gn/*.gnto flipsymbol_level,dcheck_always_on, etc.--show-planheaders, the run banner, and a configure-timeApplying N gn-arg override(s)log line; a plan withoutconfigurewarns that the flag has no effect.Design
The CLI validates each value once (
^[a-zA-Z_][a-zA-Z0-9_]*=.+— clear error naming the offending value) and threads a verbatimtuple[str, ...]throughContext.extra_gn_args; both resolver paths (resolve_configfor direct/modules-profile mode, the preset projection'sbuild_runs) set it, and configure is the only consumer. Values pass through verbatim per GN syntax: bools/ints bare, strings with embedded quotes (--gn-arg 'target_cpu="arm64"').Test plan
uv run python -m unittest discover -s bos_build -t . -p "*_test.py"— 489 tests green (new: append-last + override-ordering + log in configure_test; threading/default in resolver_test; validation,--helprepeatability, 3× show-plan headers, preset + modules-profilebuild_runsplumbing in build_test)uv run ruff check bos_builduv run browseros build --help | grep gn-arg·--list·product doctor--preset debug --gn-arg symbol_level=2 --gn-arg 'target_cpu="arm64"' --show-planshows the overrides header;--gn-arg bogusrejected with a clear error